/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package io.iec.edp.caf.data.filter;

import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.runtime.msu.ServiceUnitConfigService;
import io.iec.edp.caf.commons.transaction.service.DBTransactionService;
import io.iec.edp.caf.data.orm.jpa.DynamicTableContext;
import io.iec.edp.caf.data.orm.jpa.DynamticTableInfo;
import io.iec.edp.caf.database.DatabaseManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.time.OffsetDateTime;
import java.util.*;

/**
 * 请求返回时清理当前线程的一些线程变量，如年度表中的参数等
 */
public class CAFDataFilter extends OncePerRequestFilter {

    private Map<String,String> suNames;
    private DBTransactionService dbTransactionService;
    private final Logger logger = LoggerFactory.getLogger(CAFDataFilter.class);


    public CAFDataFilter(DBTransactionService dbTransactionService){

        this.dbTransactionService = dbTransactionService;
        suNames = new HashMap<>();
        for (String name : ServiceUnitConfigService.getEnableSu()) {
            suNames.put(name.toLowerCase(), name);
        }
    }

    @Override
    protected void doFilterInternal(
            @NonNull HttpServletRequest httpServletRequest,
            @NonNull HttpServletResponse httpServletResponse,
            @NonNull FilterChain filterChain
    ) throws ServletException, IOException {

        DatabaseManager dbm = DatabaseManager.getInstance();
        //业务处理前的处理
        //获取年度表参数
        try(DynamicTableContext dtc = DynamicTableContext.getContext()){
            DynamticTableInfo contextMap = dtc.getDynamicTableInfo();
            //检测到年度表上下文
            if(contextMap!=null&&contextMap.getDynamicTableNames()!=null&&!contextMap.getDynamicTableNames().isEmpty()){
                String path = httpServletRequest.getRequestURI();
                //throw new ServletException("系统检测到未关闭的年度表上下文，已强制关闭。涉及su名称："+this.getServiceUnitName(path));
                logger.info("系统检测到未关闭的年度表上下文，已强制清除。");
            }
            //强制关闭
            DynamicTableContext.clearContext();

            //jdbc上下文未关闭
            if(dbm.inContext()){
                dbm.end();
                throw new ServletException("请求开始时存在未关闭的DataBase，请联系管理员检查！");
            }
        }catch (Exception e){
            throw new CAFRuntimeException("caf",e.getMessage(),"ECP_CAF_FILTER_0001",e);
        }finally {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            //this.filterInvokeEventBroker.fireAfterFilterInvokeEvent(httpServletRequest, httpServletResponse);

            String path = httpServletRequest.getRequestURI();

            //业务处理后的清理
            try(DynamicTableContext dtc2 = DynamicTableContext.getContext()){
                DynamticTableInfo contextMap = dtc2.getDynamicTableInfo();
                //检测到年度表上下文
                if(contextMap!=null&&contextMap.getDynamicTableNames()!=null&&!contextMap.getDynamicTableNames().isEmpty()){
                    //throw new ServletException("系统检测到未关闭的年度表上下文，已强制关闭。涉及su名称："+this.getServiceUnitName(path));
                    logger.error("系统检测到年度表上下文未关闭，已强制关闭。涉及su名称："+this.getServiceUnitName(path));
                }
                //强制关闭
                DynamicTableContext.clearContext();
            }catch (Exception e){
                throw new CAFRuntimeException("caf",e.getMessage(),"ECP_CAF_FILTER_0001",e);
            }

            if(dbm.inContext()){
                //getIPv4() 取服务器name耗时久 调整为异步
                OffsetDateTime now = OffsetDateTime.now();
                new Thread(() -> {
                    dbTransactionService.save(path, "请求结束时存在未清理的DataBase!服务器名称和IP地址为: " + getIPv4(), now, "Request");
                }).start();
            }
        }
    }

    /**
     * 只有规范的url才过滤
     * @param request
     * @return
     */
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String path = request.getRequestURI();
        return !path.startsWith("/api/");
    }

    /**
     * 返回当前服务器的ipv4地址列表(不包含回环地址)
     */
    private List<String> getIPv4() {
        List<String> ips = null;
        boolean acquired = false;
        try {
            ips = new ArrayList<>();
            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface networkInterface = interfaces.nextElement();
                Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress address = addresses.nextElement();
                    //ipv4 && 非本地回环地址
                    if (address instanceof Inet4Address && !address.isLoopbackAddress()) {
                        if (!acquired) {
                            //getHostName耗时严重，此方法需异步调用
                            ips.add(address.getHostName());

                            acquired = true;
                        }

                        ips.add(address.getHostAddress());
                    }
                }
            }
        } catch (Exception e) {
            logger.error("未提交事务获取服务器IPv4地址异常", e);
        }
        return ips;
    }

    //通过url获取su
    private String getServiceUnitName(String url) {
        Integer index = url.indexOf("/api/");
        if (index < 0)
            return null;

        String subStr = url.substring(index + 5);
        if (subStr == null || "".equals(subStr))
            return null;
        String[] strArray = subStr.split("/");
        String suName = strArray[1];

        String suNameLowerCase = suName.toLowerCase();

        if (this.suNames.containsKey(suNameLowerCase))
            return this.suNames.get(suNameLowerCase);

        return suName;
    }
}
